package com.googlecode.androidspamblocker;

import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;

public class SmsBlocker extends Service {
	private static final String TAG = "SmsBlocker";
	private static final String URI_SMS = "content://sms/";
	private static final String URI_SMS_INBOX = "content://sms/inbox";
	private static final String THREAD_ID_COLUMN = "thread_id";
	public static final String ACTION_FILTER = "com.googlecode.androidspamblocker.ACTION_FILTER";
	public static final String ACTION_ADD = "com.googlecode.androidspamblocker.ACTION_ADD";
	public static final String ACTION_TEST = "com.googlecode.androidspamblocker.ACTION_TEST";
	private static long SMS_TIMESTAMP_THRESHOLD = 1500L;
	private SmsRuleManager ruleManager;
	private final IBinder binder = new SmsBlockerBinder();
	
	public class SmsBlockerBinder extends Binder {
		SmsBlocker getService() {
			return SmsBlocker.this;
		}
	}
	
	public SmsBlocker() {
		ruleManager = new SmsRuleManager(this);
	}
	
	@Override
	public void onCreate() {
		Log.d(TAG, "onCreate");
		ruleManager.init();
		getSpamThreadId();
	}
	
	@Override
	public void onDestroy() {
		Log.d(TAG, "onDestroy");
		ruleManager.persist();
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.d(TAG, "onStartCommand");
		if(intent != null) {
			Log.d(TAG, "got intent:" + intent.getAction());
			if(intent.getAction().equals(ACTION_FILTER)) {
				filter(intent);
			} else if(intent.getAction().equals(ACTION_ADD)) {
				ruleManager.addFilter(intent.getExtras());
			} else if(intent.getAction().equals(ACTION_TEST)) {
				Toast.makeText(this, "SmsBlocker service started", Toast.LENGTH_LONG).show();
			}
		}
		return START_STICKY;
	}
	
	private void filter(Intent intent) {
		Bundle bundle = intent.getExtras();
		if(bundle != null) {
			Object[] pdus = (Object[])bundle.get("pdus");
			SmsMessage msg[] = new SmsMessage[pdus.length];
			for(int i = 0; i < pdus.length; i++) {
				msg[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
				Log.d(TAG, "New SMS from:" + msg[i].getDisplayOriginatingAddress()
						+ " body:" + msg[i].getDisplayMessageBody());
			}
			int action = ruleManager.getAction(msg);
			switch (action) {
			case SmsAction.IN:
				Log.d(TAG, "filter in");
				break;
			case SmsAction.OUT:
				Log.d(TAG, "filter out");
				setSpam(msg);
				break;
			case SmsAction.DELETE:
				Log.d(TAG, "delete");
				deleteMessage(msg);
				break;
			case SmsAction.UNKNOWN:
				Log.d(TAG, "unknown");
				break;
			default:
				Log.d(TAG, "invalid action: " + action);
				break;
			}
		}
	}
	
	public SmsRuleManager getRuleManager() {
		return ruleManager;
	}

	private void setSpam(SmsMessage[] msg) {
		// use system timestamp instead of message timestamp because Android internally use system timstamp...
		// long timestamp = msg[0].getTimestampMillis();
		long timestamp = System.currentTimeMillis();
		try {
			Thread.sleep(SMS_TIMESTAMP_THRESHOLD);
		} catch(Exception e) {
		}
		String address = msg[0].getOriginatingAddress();
		StringBuilder builder = new StringBuilder();
		for(int i = 0; i < msg.length; i++) {
			builder.append(msg[i].getMessageBody());
		}
		Log.d(TAG, "set message to spam: timestamp=" + timestamp + " address=" + address + " body=" + builder.toString());
		Uri uri = Uri.parse(URI_SMS_INBOX);
		String spamThreadId = getSpamThreadId();
		if(spamThreadId != null) {
			ContentValues values = new ContentValues();
			values.put(THREAD_ID_COLUMN, spamThreadId);
			String where = "date>? and date<? and address=? and body=?";
			String[] args = {Long.toString(timestamp - SMS_TIMESTAMP_THRESHOLD),
					Long.toString(timestamp + SMS_TIMESTAMP_THRESHOLD),
					address,
					builder.toString()};
			int ret = getContentResolver().update(uri, values, where, args);
			Log.d(TAG, "message change to spam thread: " + ret);
		}	
	}
	
	private void deleteMessage(SmsMessage[] msg) {
		// use system timestamp instead of message timestamp because Android internally use system timstamp...
		// long timestamp = msg[0].getTimestampMillis();
		long timestamp = System.currentTimeMillis();
		try {
			Thread.sleep(SMS_TIMESTAMP_THRESHOLD);
		} catch(Exception e) {
		}
		String address = msg[0].getOriginatingAddress();
		StringBuilder builder = new StringBuilder();
		for(int i = 0; i < msg.length; i++) {
			builder.append(msg[i].getMessageBody());
		}
		Log.d(TAG, "delete spam message: timestamp=" + timestamp + " address=" + address + " body=" + builder.toString());
		Uri uri = Uri.parse(URI_SMS);
		String where = "date>? and date<? and address=? and body=?";
		String[] args = {Long.toString(timestamp - SMS_TIMESTAMP_THRESHOLD),
				Long.toString(timestamp + SMS_TIMESTAMP_THRESHOLD),
				address,
				builder.toString()};
		int ret = getContentResolver().delete(uri, where, args);
		Log.d(TAG, "message deleted: " + ret);
	}
	
	// get the thread_id of the spam thread, create one if not there.
	private synchronized String getSpamThreadId() {
		String ret = null;
		Uri uri = Uri.parse(URI_SMS_INBOX);
		String[] projection = {THREAD_ID_COLUMN};
		Cursor cursor = getContentResolver().query(uri, projection, 
				"address='Spam' AND subject='com.googlecode.androidspamblocker'", 
				null, null);
		if(cursor != null && cursor.moveToFirst()) {
			ret = cursor.getString(0);
			cursor.close();
			Log.d(TAG, "got spam thread id: " + ret);
		} else {
			Log.d(TAG, "spam thread not found, create one");
			ContentValues values = new ContentValues();
			values.put("address", "Spam");
			values.put("subject", "com.googlecode.androidspamblocker");
			values.put("body", "Messages blocked by SmsBlocker");
			uri = getContentResolver().insert(uri, values);
			if(uri != null) {
				cursor = getContentResolver().query(uri, projection, null, null, null);
				cursor.moveToFirst();
				ret = cursor.getString(0);
				cursor.close();
			} else {
				// because the SMS content provider is not part of the Android SDK, there could be devices
				// that have different SMS content provider implementations. This application simply can't
				// work properly on those devices...
				Toast.makeText(this, R.string.fail_to_create_spam_thread, Toast.LENGTH_LONG);
				//stopSelf();
			}
		}
		return ret;
	}

	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

}
